package com.speechengine;

import android.content.Intent;
import android.os.Bundle;
import android.speech.RecognitionListener;
import android.speech.RecognizerIntent;
import android.speech.SpeechRecognizer;
import android.speech.tts.TextToSpeech;
import android.speech.tts.UtteranceProgressListener;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

public class SpeechEngineModule extends ReactContextBaseJavaModule implements RecognitionListener {

    private static final String NAME = "SpeechEngine";
    private SpeechRecognizer speechRecognizer;
    private TextToSpeech textToSpeech;
    private boolean isTextToSpeechInitialized = false;
    private Promise recognitionPromise;

    public SpeechEngineModule(ReactApplicationContext reactContext) {
        super(reactContext);
        initializeTextToSpeech();
    }

    @Override
    @NonNull
    public String getName() {
        return NAME;
    }

    private void initializeTextToSpeech() {
        textToSpeech = new TextToSpeech(getReactApplicationContext(), new TextToSpeech.OnInitListener() {
            @Override
            public void onInit(int status) {
                if (status == TextToSpeech.SUCCESS) {
                    isTextToSpeechInitialized = true;
                    textToSpeech.setOnUtteranceProgressListener(new UtteranceProgressListener() {
                        @Override
                        public void onStart(String utteranceId) {
                            sendEvent("onTextToSpeechStart", null);
                        }

                        @Override
                        public void onDone(String utteranceId) {
                            sendEvent("onTextToSpeechFinish", null);
                        }

                        @Override
                        public void onError(String utteranceId) {
                            sendEvent("onTextToSpeechError", "TTS Error");
                        }
                    });
                }
            }
        });
    }

    // Speech Recognition Methods
    @ReactMethod
    public void startSpeechRecognition(String locale, Promise promise) {
        try {
            if (speechRecognizer != null) {
                speechRecognizer.destroy();
            }

            speechRecognizer = SpeechRecognizer.createSpeechRecognizer(getReactApplicationContext());
            speechRecognizer.setRecognitionListener(this);

            Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
            intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
            intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, locale);
            intent.putExtra(RecognizerIntent.EXTRA_PARTIAL_RESULTS, true);

            recognitionPromise = promise;
            speechRecognizer.startListening(intent);
            promise.resolve(null);
        } catch (Exception e) {
            promise.reject("SPEECH_RECOGNITION_ERROR", e.getMessage());
        }
    }

    @ReactMethod
    public void stopSpeechRecognition(Promise promise) {
        try {
            if (speechRecognizer != null) {
                speechRecognizer.stopListening();
            }
            promise.resolve("Speech recognition stopped");
        } catch (Exception e) {
            promise.reject("SPEECH_RECOGNITION_ERROR", e.getMessage());
        }
    }

    @ReactMethod
    public void cancelSpeechRecognition(Promise promise) {
        try {
            if (speechRecognizer != null) {
                speechRecognizer.cancel();
                speechRecognizer.destroy();
                speechRecognizer = null;
            }
            promise.resolve(null);
        } catch (Exception e) {
            promise.reject("SPEECH_RECOGNITION_ERROR", e.getMessage());
        }
    }

    @ReactMethod
    public void isSpeechRecognitionAvailable(Promise promise) {
        boolean available = SpeechRecognizer.isRecognitionAvailable(getReactApplicationContext());
        promise.resolve(available);
    }

    // Text to Speech Methods
    @ReactMethod
    public void speak(String text, ReadableMap options, Promise promise) {
        if (!isTextToSpeechInitialized) {
            promise.reject("TTS_NOT_INITIALIZED", "Text to Speech not initialized");
            return;
        }

        try {
            String language = options.hasKey("language") ? options.getString("language") : "en-US";
            float pitch = options.hasKey("pitch") ? (float) options.getDouble("pitch") : 1.0f;
            float rate = options.hasKey("rate") ? (float) options.getDouble("rate") : 1.0f;

            Locale locale = Locale.forLanguageTag(language);
            textToSpeech.setLanguage(locale);
            textToSpeech.setPitch(pitch);
            textToSpeech.setSpeechRate(rate);

            Bundle params = new Bundle();
            params.putString(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "utteranceId");
            
            int result = textToSpeech.speak(text, TextToSpeech.QUEUE_FLUSH, params, "utteranceId");
            
            if (result == TextToSpeech.SUCCESS) {
                promise.resolve(null);
            } else {
                promise.reject("TTS_ERROR", "Failed to speak");
            }
        } catch (Exception e) {
            promise.reject("TTS_ERROR", e.getMessage());
        }
    }

    @ReactMethod
    public void stop(Promise promise) {
        try {
            if (textToSpeech != null) {
                textToSpeech.stop();
            }
            promise.resolve(null);
        } catch (Exception e) {
            promise.reject("TTS_ERROR", e.getMessage());
        }
    }

    @ReactMethod
    public void pause(Promise promise) {
        // Android TTS doesn't have native pause, we'll stop instead
        stop(promise);
    }

    @ReactMethod
    public void resume(Promise promise) {
        // Android TTS doesn't have native resume
        promise.resolve(null);
    }

    @ReactMethod
    public void isSpeaking(Promise promise) {
        boolean speaking = textToSpeech != null && textToSpeech.isSpeaking();
        promise.resolve(speaking);
    }

    // RecognitionListener implementation
    @Override
    public void onReadyForSpeech(Bundle params) {
        sendEvent("onSpeechRecognitionStart", null);
    }

    @Override
    public void onBeginningOfSpeech() {
        // Speech input has begun
    }

    @Override
    public void onRmsChanged(float rmsdB) {
        // RMS value changed  
    }

    @Override
    public void onBufferReceived(byte[] buffer) {
        // Buffer received
    }

    @Override
    public void onEndOfSpeech() {
        sendEvent("onSpeechRecognitionEnd", null);
    }

    @Override
    public void onError(int error) {
        WritableMap errorMap = Arguments.createMap();
        errorMap.putString("code", String.valueOf(error));
        errorMap.putString("message", getErrorMessage(error));
        sendEvent("onSpeechRecognitionError", errorMap);
        
        if (recognitionPromise != null) {
            recognitionPromise.reject("SPEECH_RECOGNITION_ERROR", getErrorMessage(error));
            recognitionPromise = null;
        }
    }

    @Override
    public void onResults(Bundle results) {
        ArrayList<String> matches = results.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
        float[] confidenceScores = results.getFloatArray(SpeechRecognizer.CONFIDENCE_SCORES);
        
        WritableArray resultsArray = Arguments.createArray();
        
        if (matches != null) {
            for (int i = 0; i < matches.size(); i++) {
                WritableMap result = Arguments.createMap();
                result.putString("transcript", matches.get(i));
                result.putDouble("confidence", confidenceScores != null && i < confidenceScores.length ? confidenceScores[i] : 0.0);
                result.putBoolean("isFinal", true);
                resultsArray.pushMap(result);
            }
        }
        
        sendEvent("onSpeechRecognitionResults", resultsArray);
    }

    @Override
    public void onPartialResults(Bundle partialResults) {
        ArrayList<String> matches = partialResults.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
        
        WritableArray resultsArray = Arguments.createArray();
        
        if (matches != null) {
            for (String match : matches) {
                WritableMap result = Arguments.createMap();
                result.putString("transcript", match);
                result.putDouble("confidence", 0.0);
                result.putBoolean("isFinal", false);
                resultsArray.pushMap(result);
            }
        }
        
        sendEvent("onSpeechRecognitionResults", resultsArray);
    }

    @Override
    public void onEvent(int eventType, Bundle params) {
        // Handle events
    }

    private String getErrorMessage(int error) {
        switch (error) {
            case SpeechRecognizer.ERROR_AUDIO:
                return "Audio recording error";
            case SpeechRecognizer.ERROR_CLIENT:
                return "Client side error";
            case SpeechRecognizer.ERROR_INSUFFICIENT_PERMISSIONS:
                return "Insufficient permissions";
            case SpeechRecognizer.ERROR_NETWORK:
                return "Network error";
            case SpeechRecognizer.ERROR_NETWORK_TIMEOUT:
                return "Network timeout";
            case SpeechRecognizer.ERROR_NO_MATCH:
                return "No match";
            case SpeechRecognizer.ERROR_RECOGNIZER_BUSY:
                return "RecognitionService busy";
            case SpeechRecognizer.ERROR_SERVER:
                return "Error from server";
            case SpeechRecognizer.ERROR_SPEECH_TIMEOUT:
                return "No speech input";
            default:
                return "Unknown error";
        }
    }

    private void sendEvent(String eventName, @Nullable Object params) {
        getReactApplicationContext()
                .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
                .emit(eventName, params);
    }

    @Override
    public void onCatalystInstanceDestroy() {
        super.onCatalystInstanceDestroy();
        if (speechRecognizer != null) {
            speechRecognizer.destroy();
        }
        if (textToSpeech != null) {
            textToSpeech.stop();
            textToSpeech.shutdown();
        }
    }

    // For new architecture compatibility
    @ReactMethod
    public void addListener(String eventName) {
        // Keep: Required for RN built in Event Emitter Calls.
    }

    @ReactMethod
    public void removeListeners(Integer count) {
        // Keep: Required for RN built in Event Emitter Calls.
    }
}
